package in.co.codedoc.sql;

import in.co.codedoc.cg.annotations.DBType;
import in.co.codedoc.ioc.IOCContainer;
import in.co.codedoc.sql.WhereClause.Op;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

public class RelationalExpression
{
	public RelationalExpression(TableColumn left, Op op)
	{
		assert op.IsUnary() : "Expected an unary opeartor(like IS NULL,IS NOT NULL) in this usage.";
		this.left = left;
		
		this.op = op.IsUnary() ? op : Op.IS_NOT_NULL;
	}
	
	public RelationalExpression(TableColumn left, Op op, TableColumn right)
	{
		this.left = left;
		this.op = op;

		this.joinColumn = right;
	}

	public RelationalExpression(TableColumn left, Op op,SubqueryStatement subquery)
	{
		this.left = left;
		this.op = op;

		this.subquery = subquery;
	}

	public RelationalExpression(TableColumn left, Op op, String v)
	{
		this.left = left;
		this.op = op;
		
		this.columnMap 
			= IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}

	public RelationalExpression(TableColumn left, Op op, short v)
	{
		this.left = left;
		this.op = op;

		this.columnMap 
			= IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}

	public RelationalExpression(TableColumn left, Op op, int v)
	{
		this.left = left;
		this.op = op;

		this.columnMap 
			= IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}

	public RelationalExpression(TableColumn left, Op op, long v)
	{
		this.left = left;
		this.op = op;

		this.columnMap = IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}

	public RelationalExpression(TableColumn left, Op op, IDBase v)
	{
		this.left = left;
		this.op = op;

		this.columnMap = IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}
	
	public RelationalExpression(TableColumn left, Op op, double v)
	{
		this.left = left;
		this.op = op;

		this.columnMap = IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v);
	}

	public RelationalExpression(TableColumn left, Op op, Object v,DBType dbType)
	{
		this.left = left;
		this.op = op;

		this.columnMap = IOCContainer.Lookup(DBMapUtil.class).GetInputColumnMap(v,dbType);
	}

	public RelationalExpression(TableColumn left, Op op,DBInputColumnMap columnMap)
	{
		this.left = left;
		this.op = op;

		this.columnMap = columnMap;
	}
	
	public TableColumn GetLeft()
	{
		return left;
	}
	
	public boolean IsJoin()
	{
		return joinColumn != null;
	}	
	
	public TableColumn GetJoinColumn()
	{
		return joinColumn;
	}
	
	public boolean HasSubquery()
	{
		return subquery != null;
	}
	
	public SubqueryStatement GetSubquery()
	{
		return subquery;
	}
	
	public boolean HasColumnMap()
	{
		return columnMap != null;
	}
	
	public DBInputColumnMap GetColumnMap()
	{
		return columnMap;
	}
	
	private TableColumn left;
	
	private WhereClause.Op op;
	
	private TableColumn joinColumn;
	private SubqueryStatement subquery;
	private DBInputColumnMap columnMap;
	public PrintWriter Generate(PrintWriter out)
		throws IOException
	{
		left.Generate(out);
		out.write(' ');
		op.Generate(out);
		
		if(IsJoin())
		{
			out.write(' ');
			joinColumn.Generate(out);
		}
		else if(HasSubquery())
		{
			out.write(" (");
			subquery.Generate(out);
			out.write(')');
		}
		else if(HasColumnMap())
		{
			out.write(" ?");
		}
		else
		{
			assert op.IsUnary() : "Expected one of JOIN SUBQUERY or a COLUMN MAP.";
		}

		return out;
	}

	public void GetParameterValues(LinkedList<DBInputColumnMap> values)
	{
		if(IsJoin())
		{
			//Nothing
		}
		else if(HasSubquery())
		{
			subquery.GetParameterValues(values);
		}
		else if(HasColumnMap())
		{
			values.add(GetColumnMap());
		}
		else
		{
			assert op.IsUnary() : "Expected one of JOIN SUBQUERY or a COLUMN MAP.";
		}
	}

	public Set<Table> GetTables()
	{
		Set<Table> tables = new HashSet<Table>();
		
		tables.add(GetLeft().GetTable());
		if(IsJoin())
		{
			tables.add(GetJoinColumn().GetTable());
		}
		else if(HasSubquery())
		{
			tables.addAll(subquery.Where().GetTables());
		}
		
		return tables;
	}
}
